import os
import rest # appengine-rest-server (NOT USED ANYMORE)
import logging

from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.ext.webapp import template

import xml.dom.minidom

COMMENT_CONTENT = 0
CSP_CONTENT = 1
PYTHON_CONTENT = 2

# can support multiple component-upload
def parseBody(payload):
    #content = []
    compdata = {}
    doc = xml.dom.minidom.parseString(payload)
    root = doc.firstChild
    #for comp in root.getElementsByTagName('component'):
    for comp in root.getElementsByTagName('component'):
        #compdata = {}
        for e in comp.childNodes:
                
            if e.nodeName == 'key':
                compdata['key'] = e.firstChild.data
                    
            elif e.nodeName == 'name':
                compdata['name'] = e.firstChild.data
                    
            elif e.nodeName == 'package':
                compdata['package'] = e.firstChild.data
                
            elif e.nodeName == 'users':
                compdata['users'] = []
                for item in e.getElementsByTagName('user'):
                    user = item.firstChild.data
                    #user = users.User(item.firstChild.data)
                    #logging.info('Trying to add user: ' + user.email() + ' with id ' + user.user_id())
                    #if not user.user_id():
                    #    logging.info('User: ' + user.email() + ' not found, so not added.')
                    #else:
                    #    logging.info('Adding user with id ' + user.user_id())
                    compdata['users'].append(user)
                    
            elif e.nodeName == 'csp':
                compdata['csp'] = e.firstChild.data
                    
            elif e.nodeName == 'python':
                compdata['python'] = e.firstChild.data
                
            elif e.nodeName == 'comment':
                compdata['comment'] = e.firstChild.data
                
        #content.append(compdata)
            
    return compdata

def buildXMLDoc(components):
    doc = xml.dom.minidom.Document()
    root = doc.createElement('doc')
    doc.appendChild(root)
    for c in components:
        compElem = doc.createElement("component")
        
        # key
        elem = doc.createElement("key")
        txtElem = doc.createTextNode(str(c.key()))
        #print '1', str(c.key())
        elem.appendChild(txtElem)
        compElem.appendChild(elem)
        
        # name
        elem = doc.createElement("name")
        #print '2', c.name
        txtElem = doc.createTextNode(c.name)
        elem.appendChild(txtElem)
        compElem.appendChild(elem)
        
        # package
        elem = doc.createElement("package")
        txtElem = doc.createTextNode(c.package)
        #print '3', c.package
        elem.appendChild(txtElem)
        compElem.appendChild(elem)
        
        # latest version
        elem = doc.createElement("latestVer")
        txtElem = doc.createTextNode(str(c.latestVersion))
        elem.appendChild(txtElem)
        compElem.appendChild(elem)
        
        # users
        elem = doc.createElement("users")
        for u in c.users:
            item = doc.createElement('user')
            txtElem = doc.createTextNode(u)
            item.appendChild(txtElem)
            elem.appendChild(item)    
        compElem.appendChild(elem)
        
        # comments, csp and python code
        # content_set holds back-references to all content
        for content in c.content_set:
            if content.contenttype == COMMENT_CONTENT:
                elem = doc.createElement("comment")
            elif content.contenttype == CSP_CONTENT:
                elem = doc.createElement("csp")
            elif content.contenttype == PYTHON_CONTENT:
                elem = doc.createElement("python")  
                
            elem.setAttribute('version', str(content.version))
            txtElem = doc.createTextNode(content.content)
            elem.appendChild(txtElem)
            compElem.appendChild(elem)
            
        root.appendChild(compElem)
        
    return doc

        
class ComponentHandler(webapp.RequestHandler):
    '''
    Handler to take care of getting and posting
    components.
    '''
    
    def get(self):
        
        # get user
        user = users.get_current_user()
        
        #if key:
        if self.request.headers.has_key('Key'):
            key = self.request.headers['Key']
            logging.info('Got request for component with key: ' + key + '. User is ' + user.email())
            component = Component.get(key)
            
            #authenticated = False
            #for u in component.users:
            #    if u.user_id() == user.user_id():
            #        authenticated = True
            #        break
            if not component.users.count(user.email()):
            #if not authenticated:
                self.response.set_status(401)
                return
            else:
                usersXML = '<doc><users>'
                for u in component.users:
                    usersXML = usersXML + '<user>' + u + '</user>'
                usersXML = usersXML + '</users></doc>'
                self.response.out.write(usersXML)
        else:
            logging.info('Got request for all components')
            components = Component.all().fetch(1000)
            
            # create XML to output        
            doc = buildXMLDoc(components)
        
            # write output
            self.response.out.write(doc.toxml())
    
    def post(self):
        
        # get user, not necessary to check if user is logged in, as this is required in yaml
        user = users.get_current_user()
        
        payload = self.request.body
        newComp = parseBody(payload)
        
        if newComp.has_key('key'):
            
            # user trying to update specific component: check permission
            component = Component.get(newComp['key'])
            #authenticated = False
            #for u in component.users:
            #    if u.user_id() == user.user_id():
            #        authenticated = True
            #        break
            if not component.users.count(user):
            #if not authenticated:
                self.response.set_status(401)
                return
            
            # update version component
            version = component.latestVersion + 1
            component.latestVersion = version            
                
        else:
            # create new component
            version = 1
            component = Component(name=newComp['name'],
                          package=newComp['package'])
            
        # update component users
        for u in newComp['users']:
            if not component.users.count(u):
                component.users.append(u)
                
        # commit changed to component
        key = component.put()
                
        # csp
        cspContent = Content(component=component,contenttype=CSP_CONTENT,
                                 version=version,content=newComp['csp'])
        cspContent.put()
            
        # python code
        if newComp.has_key('python'):
            pyContent = Content(component=component,contenttype=PYTHON_CONTENT,
                                version=version,content=newComp['python'])
            pyContent.put()
            
        # comment
        if newComp.has_key('comment'):
            comment = Content(component=component,contenttype=COMMENT_CONTENT,
                                version=version,content=newComp['comment'])
            comment.put()
            
        
        # return key and version
        response = '<doc><key>' + str(key) + '</key><version>' + str(version) + '</version></doc>'
        self.response.out.write(response)

class Component(db.Expando):
    '''
    Datamodel for a component.
    '''
    
    created = db.DateTimeProperty(auto_now_add=True)
    name = db.StringProperty(required=True)
    package = db.StringProperty(required=True)
    latestVersion = db.IntegerProperty(default=1)
    users = db.StringListProperty()
    #users = db.ListProperty(users.User)
    
class Content(db.Model):
    component = db.ReferenceProperty(Component)
    contenttype = db.IntegerProperty(required=True)
    version = db.IntegerProperty(required=True)
    content = db.TextProperty()


application = webapp.WSGIApplication([('/rest/.*', rest.Dispatcher),
                                      ('/component', ComponentHandler)],debug=True)

# REST SERVER IS NOT USED!
# configure the rest dispatcher to know what prefix to expect on request urls
rest.Dispatcher.base_url = "/rest"

# add specific models to rest dispatcher
rest.Dispatcher.add_models({"component": Component,
                            "content": Content})

def main():
    logging.getLogger().setLevel(logging.DEBUG)
    run_wsgi_app(application)

if __name__ == '__main__':
    main()